// checksite 是用来检测数据库中站点网址是否能够正确的访问，并将更新数据库对应信息状态，发送异常信息给监控人员
package main

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/mail"
	"net/url"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"
)

type Conf struct {
	MailStmp St
	Mailfrom mail.Address
	Mailtos  []mail.Address
	Database Dbconf
}

var Noip string
var conf Conf
var dbcon string
var timeout = time.Duration(5 * time.Second)

func init() {
	getconf()
	dbcon = conf.Database.User + ":" + conf.Database.Pwd + "@tcp(" + conf.Database.Host + ")/" + conf.Database.DbName + "?charset=utf8"
	// 一个403ip
	nohost, _ := url.Parse("http://xx.xx")
	ip, _ := net.LookupIP(nohost.Host)
	if len(ip) > 0 {
		Noip = ip[0].String()
	} else {
		Noip = ""
	}
}

func main() {

	var logs string = ""
	var mailbody string = ""

	sites := getSites()
	// var site Site
	for _, site := range sites {
		host := site.url
		addr, uip := To_Url(host)
		if uip == Noip {
			msg := fmt.Sprintf("\n ID: %d \n 站点: %s \n 地址: %s 无法解析 ", site.id, site.title, site.url)
			log.Printf("\n ID: %d \n 站点: %s \n 地址: %s 无法解析 ", site.id, site.title, site.url)
			if site.state != 0 && setSite(site.id, 0) {
				msg += fmt.Sprintf("\n 更新id为 %d 数据...\n", site.id)
				log.Printf(" 更新id为 %d 数据...\n", site.id)
			} else {
				msg += fmt.Sprintf("\n ID: %d 数据问题之前已存在...\n", site.id)
				log.Printf(" ID: %d 数据问题之前已存在...\n", site.id)
			}
			mailbody += msg
			logs += msg
		} else {
			msg := fmt.Sprintf("\n ID: %d \n 站点: %s \n 域名:%s \n IP: %s", site.id, site.title, addr, uip)
			log.Printf("\n ID: %d \n 站点: %s \n 域名:%s \n IP: %s", site.id, site.title, addr, uip)
			sitelog, state := get(addr)
			if state == 0 {
				msg += fmt.Sprintf("\n 访问异常... ")
				log.Printf(" 访问异常... ")
				if site.state != 0 && setSite(site.id, 0) {
					msg += fmt.Sprintf("\n 更新id为 %d 数据...\n", site.id)
					log.Printf(" 更新id为 %d 数据...\n", site.id)
				} else {
					msg += fmt.Sprintf("\n ID: %d 数据问题之前已存在...\n", site.id)
					log.Printf(" ID: %d 数据问题之前已存在...\n", site.id)
				}
				mailbody += msg
			}
			if state == 1 && site.state == 0 {
				if setSite(site.id, state) {
					msg += fmt.Sprintf("\n 站点恢复正常... \n 更新id为 %d 数据...\n", site.id)
					log.Printf(" 站点恢复正常... \n 更新id为 %d 数据...\n", site.id)
				}
				mailbody += msg
			}
			logs += msg
			logs += sitelog
			fmt.Printf(" GET: %s \n", sitelog)
		}
	}

	if mailbody != "" {
		// 邮件发送
		for _, to := range conf.Mailtos {
			domail(to, "监控:"+time.Now().Format("2006-01-02 15:04:05"), mailbody)
			logs += fmt.Sprintf("\n\n ====> \n已发送邮件给[%s](%s) \n 时间 %s", to.Name, to.Address, time.Now().Format("2006-01-02 15:04:05"))
			log.Printf(" 已发送邮件给[%s](%s)。时间 %s\n", to.Name, to.Address, time.Now().Format("2006-01-02 15:04:05"))
		}
	} else {
		logs += fmt.Sprintf("\n \n 没变动,不发送报告 \n 时间: %s", time.Now().Format("2006-01-02 15:04:05"))
		log.Printf(" 没变动,不发送报告。 时间: %s", time.Now().Format("2006-01-02 15:04:05"))
	}

	logname := GetPath() + "/log/" + time.Now().Format("2006-01-02 15-04-05") + ".log"
	ioutil.WriteFile(logname, []byte(logs), 0600)

}

// timeout setting
func dialTimeout(network, address string) (net.Conn, error) {
	return net.DialTimeout(network, address, timeout)
}

func get(addr string) (logs string, state int) {
	state = 1
	logs = ""

	transport := &http.Transport{
		Dial: dialTimeout,
	}

	client := http.Client{
		Transport:     &transport,
		CheckRedirect: func(_ *http.Request, _ []*http.Request) error { return fmt.Errorf("Redirect Disabled") },
	}

	resp, err := client.Get(addr)

	if err != nil {
		logs = " " + err.Error()
		state = 0
		return
	}

	logs += fmt.Sprintf("\n 地址: %s", addr)
	logs += fmt.Sprintf("\n 返回状态: %s", resp.Status)
	// fmt.Printf("\n Header: %v", resp.Header)
	logs += fmt.Sprintf("\n 返回头: \n\t 访问时间:%s \n\t 服务器: %s \n\t 驱动: %s \n\t 文件类型:%s", resp.Header.Get("Date"), resp.Header.Get("Server"), resp.Header.Get("X-Powered-By"), resp.Header.Get("Content-Type"))

	if resp.StatusCode != 200 {
		logs = "header err"
		state = 0
		return
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	checkErr(err)
	text := string(body)

	re, _ := regexp.Compile("\\s{2,}")
	text = re.ReplaceAllString(text, "\n")

	re, _ = regexp.Compile("<title>([\n]{0,1})(.*)([\n]{0,1})</title>")
	title := strings.TrimSpace(re.FindString(text))
	if title != "" {
		title = strings.Replace(title, "<title>", "", 1)
		title = strings.Replace(title, "</title>", "", 1)
		logs += fmt.Sprintf("\n 标题:%v", strings.TrimSpace(title))
	}

	logs += fmt.Sprintf("\n 现在时间: %s \n", time.Now().Format("2006-01-02 15:04:05"))

	return
}

func checkErr(err error) {
	if err != nil {
		// panic(err)
		ioutil.WriteFile("error.txt", []byte(err.Error()), 0600)
	}
}

func timeline() string {
	return strconv.FormatInt(time.Now().Unix(), 10)
}

func readLines(path string) ([]string, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	var lines []string
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}
	return lines, scanner.Err()
}

// 返回处理地址 , 和 host 对应IP地址
func To_Url(host string) (addr string, ip string) {
	addr = host
	u, _ := url.Parse(addr)
	if u.Host == "" {
		u, _ = url.Parse("http://" + addr)
	}
	if u.Host != "" {
		addr = "http://" + u.Host
	}

	if !Is_Http(addr) {
		return "", Noip
	}

	uip, _ := net.LookupIP(u.Host)
	if len(uip) > 0 {
		ip = uip[0].String()
	} else {
		ip = ""
	}
	return addr, ip
}

// 验证email
func Is_Http(s string) bool {
	if m, _ := regexp.MatchString(`^http:\/\/([\w\.\_\-]{2,}).([a-z]{2,4})$`, s); !m {
		return false
	} else {
		return true
	}
}

func GetPath() string {
	wd, _ := filepath.Abs(os.Args[0])
	wd = filepath.Dir(wd)
	return wd
}
